package com.lagou.excutor;

import com.lagou.config.BoundSql;
import com.lagou.config.Configuration;
import com.lagou.config.MapperStatement;
import com.lagou.utils.GenericTokenParser;
import com.lagou.utils.ParameterMapping;
import com.lagou.utils.ParameterMappingTokenHandler;

import javax.sql.DataSource;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * Excutor执行器，执行jdbc的代码
 */
public class SimpleExecutor implements Executor {

    public <E> List<E> query(Configuration configuration, String statementId, Object... params) throws Exception {
        //1.注册驱动，获取jdbc连接
        DataSource dataSource = configuration.getDataSource();
        Connection connection = dataSource.getConnection();
        Map<String,MapperStatement> mapperStatementMap = configuration.getMapperStatementMap();
        MapperStatement mapperStatement = mapperStatementMap.get(statementId);
        //2.获取SQL语句
        /**
         * select id,name from user where id = #{id} and name = #{name}
         */
        String sqlText = mapperStatement.getSqlText();
        /**
         * 处理后SQL：select id,name from user where id = ? and name = ?
         */
        BoundSql boundSql = getBoundSql(sqlText);
        //3.获取预处理对象
        String sql = boundSql.getSqlText();
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        //4.设置参数
        Class<?> parameterTypeClass = getClassType(mapperStatement.getParameterType());
        List<ParameterMapping> parameterMappingList = boundSql.getParameterMappings();
        if(parameterMappingList.size() == 1) {
            preparedStatement.setObject(1,params[0]);
        }else {
            for(int i=0;i<parameterMappingList.size();i++) {
                ParameterMapping parameterMapping = parameterMappingList.get(i);
                //根据属性名称获取属性对象
                Field field = parameterTypeClass.getDeclaredField(parameterMapping.getContent());
                //暴力访问，可以访问到private属性字段
                field.setAccessible(true);
                //获取属性值，params[0]为需要获取值的对象
                Object obj = field.get(params[0]);
                preparedStatement.setObject(i+1,obj);
            }
        }
        //5.执行SQL，返回ResultSet
        ResultSet resultSet = preparedStatement.executeQuery();
        //6.遍历结果集，封装返回对象
       List<Object> resultList = new ArrayList<>();
        //获取返回值类型字节码对象
        String resultType = mapperStatement.getResultType();
        Class<?> resultTypeClass = getClassType(resultType);
        while (resultSet.next()) {
            Object result = resultTypeClass.newInstance();
            ResultSetMetaData metaData = resultSet.getMetaData();
            //获取SQL中列的个数
            int columnCount = metaData.getColumnCount();
            for(int i=0;i<columnCount;i++) {
                //列的index从1开始，获取列名
                String columnName = metaData.getColumnName(i+1);
                Object columnVal = resultSet.getObject(columnName);
                //通过内省机制获取设置列的值
                PropertyDescriptor propertyDescriptor = new PropertyDescriptor(columnName,resultTypeClass);
                //获取属性的setter方法对象，并且设置属性值
                Method writeMethod = propertyDescriptor.getWriteMethod();
                writeMethod.invoke(result,columnVal);
            }
            resultList.add(result);
        }
        return (List<E>) resultList;
    }

    @Override
    public int update(Configuration configuration, String statementId, Object... params) throws Exception {
        //1.注册驱动，获取jdbc连接
        DataSource dataSource = configuration.getDataSource();
        Connection connection = dataSource.getConnection();
        Map<String,MapperStatement> mapperStatementMap = configuration.getMapperStatementMap();
        MapperStatement mapperStatement = mapperStatementMap.get(statementId);
        //2.获取SQL语句
        /**
         * select id,name from user where id = #{id} and name = #{name}
         */
        String sqlText = mapperStatement.getSqlText();
        /**
         * 处理后SQL：select id,name from user where id = ? and name = ?
         * id,name
         */
        BoundSql boundSql = getBoundSql(sqlText);
        //3.获取预处理对象
        String sql = boundSql.getSqlText();
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        //4.设置参数
        Class<?> parameterTypeClass = getClassType(mapperStatement.getParameterType());
        List<ParameterMapping> parameterMappingList = boundSql.getParameterMappings();
        if(parameterMappingList.size() == 1) {
            preparedStatement.setObject(1,params[0]);
        }else {
            for(int i=0;i<parameterMappingList.size();i++) {
                ParameterMapping parameterMapping = parameterMappingList.get(i);
                //根据属性名称获取属性对象
                Field field = parameterTypeClass.getDeclaredField(parameterMapping.getContent());
                //暴力访问，可以访问到private属性字段
                field.setAccessible(true);
                //获取属性值，params[0]为需要获取值的对象
                Object obj = field.get(params[0]);
                preparedStatement.setObject(i+1,obj);
            }
        }
        //5.执行SQL
        preparedStatement.execute();
        return preparedStatement.getUpdateCount();
    }

    private Class<?> getClassType(String parameterType) throws ClassNotFoundException {
        if(null == parameterType || "".equals(parameterType.trim())) {
            return null;
        }
        return Class.forName(parameterType);
    }

    private BoundSql getBoundSql(String sqlText) {
        ParameterMappingTokenHandler tokenHandler = new ParameterMappingTokenHandler();
        GenericTokenParser genericTokenParser = new GenericTokenParser("#{","}",tokenHandler);
        String sql = genericTokenParser.parse(sqlText);
        BoundSql boundSql = new BoundSql();
        boundSql.setSqlText(sql);
        boundSql.setParameterMapping(tokenHandler.getParameterMappings());
        return boundSql;
    }
}
